#include "bomb_projectile.h"

namespace BombProjectile
{
    bool destroyEntities( CEntity *pBomb, int nHeight, int nWidth, int nDepth )
    {
        /**
         * Destroy entities (destructible tiles and players) at a given
         * tile location. If this destroys anything, then return true.
         *
         * @param int nHeight The height position of the tile
         * @param int nWidth The width position of the tile
         * @param int nDepth The depth position of the tile
         * @return bool Whether or not an entity was destroyed
         */
        CMap *pMap(CMapContainer::getInstance()->getCurrentMap());

        if (! pMap->isValidTile(nHeight, nWidth, nDepth))
        {
            // We can't destroy anything in a non-valid tile
            return false;
        }
        CTile *pTile(pMap->getTile(nHeight, nWidth, nDepth));
        if (pTile->isBeingDestroyed())
        {
            return false;
        }
        pTile->destroy();

        if (pTile->isDestructible())
        {
            dynamic_cast<CPlayer*>(
                pBomb->getOwner())->updateStatValue(
                "boxes destroyed", 1);
            return false;
        }

        if (!pTile->isWalkable())
        {
            return false;
        }
        else
        {
            bool bReturnEarly(false);
            vector<CEntity*> arrEntities(pTile->getAllEntitiesInBlock());
            for (vector<CEntity*>::const_iterator pEntity(
                    arrEntities.begin()); pEntity != arrEntities.end(); 
                    ++pEntity)
            {
                if ((*pEntity)->getID() == ENT_PROJECTILE_BOMB)
                {
                    CBombProjectile *pBombTemp(
                        dynamic_cast<CBombProjectile*>(*pEntity));
                    if (! pBombTemp->isExploding())
                    {
                        pBombTemp->explode();
                    }
                }
                else if ((*pEntity)->getID() == ENT_PLAYER)
                {
                    if (!(CDeviceManager::getInstance()->getCheatMode() &
                        Constants::GODMODE))
                    {
                        int nH(0), nW(0), nD(0);
                        pMap->getTilePositionFromCoord(
                            nH, nW, nD,
                            (*pEntity)->getX(), (*pEntity)->getY(), 
                            (*pEntity)->getZ(), 0);
                                
                        if (pMap->isValidTile(nH, nW, nD) &&
                            nH == nHeight &&
                            nW == nWidth &&
                            nD == nDepth)
                        {
                            CPlayer *pPlayer(dynamic_cast<CPlayer*>(*pEntity));
                            bReturnEarly = true;
                            if (! pPlayer->isDying())
                            {
                                pPlayer->kill();
                                
                                if (pPlayer != pBomb->getOwner())
                                {
                                    dynamic_cast<CPlayer*>(
                                        pBomb->getOwner())->updateStatValue(
                                        "kills", 1);
                                }
                            }
                        }
                    }
                }
                else if ((*pEntity)->isCollectible())
                {
                    (*pEntity)->collect(nullptr);
                }
            }
            if (bReturnEarly)
            {
                return false;
            }
        }
        return true;
    }

    void explode(DELAY_PARAMS &params)
    {
        /**
         * Explodes the bomb where it is and takes out everything in the radius
         * where it needs to.
         *
         * @param DELAY_PARAMS params
         */
        CBombProjectile *pBombProjectile(
            GET_POINTER_FROM_PARAM(params, 0, CBombProjectile));
        if (pBombProjectile == nullptr ||
            pBombProjectile->isExploding())
        {
            return;
        }
        pBombProjectile->setExplode();

        CBomb *pBomb(reinterpret_cast<CBomb*>(pBombProjectile->getOwner()));

        float fX(0), fY(0), fZ(0);
        int nHeight(0), nWidth(0), nDepth(0);
        pBombProjectile->getPosition(fX, fY, fZ);

        CMap *pMap(CMapContainer::getInstance()->getCurrentMap());

        if (pMap != nullptr)
        {
            pMap->getTilePositionFromCoord(
                nHeight, nWidth, nDepth, fX, fY, fZ, 0);

            stringstream sstream;
            sstream << "bomb_explode_"
                    << (pBomb->getExplodeRadius() <= 1 ? "s" : 
                       (pBomb->getExplodeRadius() <= 3  ? "m" : "l"))
                    << ".wav";

            CSound *pSound(CSoundManager::getInstance()->addSound(
                sstream.str()));
            FMOD::Channel *pChannel(nullptr);
            pSound->createNewChannel(&pChannel);
            pChannel->setPaused(false);

            // Bomb block
            if (pMap->isValidTile(nHeight, nWidth, nDepth))
            {
                destroyEntities(pBomb, nHeight, nWidth, nDepth);
            }

            // Above blocks
            for (int i(1); i <= pBomb->getExplodeRadius(); ++i)
            {
                if (! BombProjectile::destroyEntities(
                        pBomb, nHeight + i, nWidth, nDepth))
                {
                    break;
                }
            }

            // Below blocks
            for (int i(1); i <= pBomb->getExplodeRadius(); ++i)
            {
                if (! BombProjectile::destroyEntities(
                        pBomb, nHeight - i, nWidth, nDepth))
                {
                    break;
                }
            }
                

            // To the right blocks
            for (int i(1); i <= pBomb->getExplodeRadius(); ++i)
            {
                if (! BombProjectile::destroyEntities(
                        pBomb, nHeight, nWidth + i, nDepth))
                {
                    break;
                }
                
            }

            // To the left blocks
            for (int i(1); i <= pBomb->getExplodeRadius(); ++i)
            {
                if (! BombProjectile::destroyEntities(
                        pBomb, nHeight, nWidth - i, nDepth))
                {
                    break;
                }
            }

            // Behind blocks
            for (int i(1); i <= pBomb->getExplodeRadius(); ++i)
            {
                if (! BombProjectile::destroyEntities(
                        pBomb, nHeight, nWidth, nDepth + i))
                {
                    break;
                }
                
            }

            // Front blocks
            for (int i(1); i <= pBomb->getExplodeRadius(); ++i)
            {
                if (! BombProjectile::destroyEntities(
                        pBomb, nHeight, nWidth, nDepth - i))
                {
                    break;
                }
            }
        }

        /*if (pMap->isValidTile(nHeight, nWidth, nDepth))
        {
            CTile *pTile(pMap->getTile(nHeight, nWidth, nDepth));
            pTile->removeEntityFromBlock(pBombProjectile);
            pTile->destroy();
        }*/

        pBomb->setClipCount(pBomb->getClipCount() + 1);

        CDisplayableContainer::getInstance()->removeDisplayable(
            pBombProjectile->getDisplayable());

        CEntityContainer::getInstance()->removeEntity(
            pBombProjectile);

        
    }
};

CBombProjectile::CBombProjectile(float fX, float fY, float fZ) :
    CProjectile("weapons/bomb", ENT_PROJECTILE_BOMB, fX, fY, fZ),
    m_explodeRepeat(3000.0f, &BombProjectile::explode, 1),
    m_bExploding(false)
{
    /**
     * @param float fX The X position of the projectile
     * @param float fY The Y position of the projectile
     * @param float fZ The Z position of the projectile
     */
    m_explodeRepeat.registerParam(this, utils::TYPE_OTHER, sizeof(*this));
    m_explodeRepeat.start();
}

CBombProjectile::~CBombProjectile()
{
    /**
     * Destructor, stop any of the active repeats.
     */
    m_explodeRepeat.stop();
}

void CBombProjectile::explode()
{
    /**
     * Explode a bomb projectile early.
     */
    if (m_explodeRepeat.isRunning() && ! isExploding())
    {
        m_explodeRepeat.stop(true);
    }
}

bool CBombProjectile::isExploding() const
{
    return m_bExploding;
}

void CBombProjectile::setExplode()
{
    m_bExploding = true;
}

void CBombProjectile::tick()
{
    /**
     * Executed each tick cycle. We need to ensure the repeat is ticked.
     */
    CProjectile::tick();

    m_explodeRepeat.tick();
}